package com.yn.sample.execution.objectfactory;

import java.lang.reflect.Field;
import java.util.*;

public class ClassIntrospectorUtil {
    private static LinkedHashMap<Class, ClassInfo> classInfoLinkedHashMap = new LinkedHashMap<>();


    public static void initSpecifiedClass(Class<?> clazz) {
        ClassInfo classInfo = new ClassInfo();

        List<Field> allFiled = getAllFiled(clazz);

        /**
         * 计算并设置该class类型对象占用的空间大小
         */
        {
            int totalSize = calculateSize(allFiled);
            classInfo.setTotalSize(totalSize);
        }

        /**
         * 计算各field的offset信息
         */
        LinkedHashMap<String, FieldInfo> map = getFieldOffsetMap(allFiled);
        classInfo.setFieldInfoMap(map);

        classInfoLinkedHashMap.put(clazz,classInfo);
    }

    /**
     * 根据class，获取对应的class的信息
     * @param clazz
     * @return
     */
    public static ClassInfo getByClass(Class<?> clazz) {
        ClassInfo classInfo = classInfoLinkedHashMap.get(clazz);
        if (classInfo == null) {
            initSpecifiedClass(clazz);
            return classInfoLinkedHashMap.get(clazz);
        }

        return classInfo;
    }

    private static LinkedHashMap<String, FieldInfo> getFieldOffsetMap(List<Field> allFiled) {
        LinkedHashMap<String, FieldInfo> map = new LinkedHashMap<>();
        int currentIndex = 0;
        for (Field field : allFiled) {

            /**
             * 设置当前field的offset等信息
             */
            FieldInfo fieldInfo = new FieldInfo();
            fieldInfo.setFieldName(field.getName());
            fieldInfo.setFieldOffset(currentIndex);
            fieldInfo.setField(field);

            map.put(field.getName(),fieldInfo);

            Class<?> fieldType = field.getType();
            if (fieldType.isPrimitive()) {
                if (fieldType == Integer.TYPE) {
                    currentIndex += 4;
                } else if (fieldType == Byte.TYPE) {
                    currentIndex += 1;
                } else if (fieldType == Long.TYPE) {
                    currentIndex += 8;
                } else if (fieldType == Float.TYPE) {
                    currentIndex += 4;
                } else if (fieldType == Double.TYPE) {
                    currentIndex += 8;
                } else if (fieldType == Short.TYPE) {
                    currentIndex += 2;
                } else if (fieldType == Character.TYPE) {
                    currentIndex += 2;
                } else if (fieldType == Boolean.TYPE) {
                    currentIndex += 1;
                }
            } else if (fieldType.isArray()) {
                /**
                 * 当成一个引用
                 */
                currentIndex += 4;
            } else {
                /**
                 * 引用数据类型
                 */
                currentIndex += 4;
            }
        }

        return map;
    }

    /**
     * 分配指定class的
     * @return
     * @param allFiled
     */
    public static int calculateSize(List<Field> allFiled) {
        HashMap<String, FieldInfo> fieldOffSetMap = new HashMap<>();

        int totalSize = 0;
        for (Field field : allFiled) {
            Class<?> fieldType = field.getType();

            FieldInfo fieldInfo = new FieldInfo();
            fieldInfo.setFieldName(field.getName());
            fieldInfo.setFieldOffset(totalSize);
            fieldInfo.setField(field);

            fieldOffSetMap.put(field.getName(),fieldInfo);

            if (fieldType.isPrimitive()) {
                if (fieldType == Integer.TYPE) {
                    totalSize += 4;
                } else if (fieldType == Byte.TYPE) {
                    totalSize += 1;
                } else if (fieldType == Long.TYPE) {
                    totalSize += 8;
                } else if (fieldType == Float.TYPE) {
                    totalSize += 4;
                } else if (fieldType == Double.TYPE) {
                    totalSize += 8;
                } else if (fieldType == Short.TYPE) {
                    totalSize += 2;
                } else if (fieldType == Character.TYPE) {
                    totalSize += 2;
                } else if (fieldType == Boolean.TYPE) {
                    totalSize += 1;
                }
            } else if (fieldType.isArray()) {
                /**
                 * 当成一个引用
                 */
                totalSize += 4;
            } else {
                /**
                 * 引用数据类型
                 */
                totalSize += 4;
            }
        }

        totalSize = padding(totalSize);

        return totalSize;
    }



    /**
     * 4字节对齐
     * @param cap
     * @return
     */
    static final int padding(int cap) {
        while (cap % 4 != 0) {
            cap++;
        }

        return cap;
    }

    /**
     * 获取该类的全部field，包括private、public、package、protected等各种权限
     * 同时，会递归获取父接口、父类
     * 参考了：
     * {@link Class#getFields()} ()} 中的递归处理
     *
     * @param clazz
     * @return
     */
    public static List<Field> getAllFiled(Class<?> clazz){
        List<Field> allFiled = getAllFiled(clazz, null);
        allFiled.sort(Comparator.comparing(Field::getName));

        return allFiled;
    }

    private static List<Field> getAllFiled(Class<?> clazz, Set<Class<?>> traversedInterfaces) {
        if (traversedInterfaces == null) {
            traversedInterfaces = new HashSet<>();
        }

        ArrayList<Field> fields = new ArrayList<>();
        /**
         * 1 获取这个类自身声明的fields
         */
        Field[] declaredFields = clazz.getDeclaredFields();
        addAll(fields, Arrays.asList(declaredFields));

        // Direct superinterfaces, recursively
        for (Class<?> c : clazz.getInterfaces()) {
            if (!traversedInterfaces.contains(c)) {
                traversedInterfaces.add(c);
                addAll(fields, getAllFiled(c,traversedInterfaces));
            }
        }

        // Direct superclass, recursively
        if (!clazz.isInterface()) {
            Class<?> c = clazz.getSuperclass();
            if (c != null) {
                addAll(fields, getAllFiled(c,traversedInterfaces));
            }
        }

        return fields;
    }


    private static void addAll(Collection<Field> c, Collection<Field> o) {
        c.addAll(o);
    }



    static class  child extends Parent{
        int fOfChild;
        double dOfChild;
    }
    static class  Parent{
        int p;
        short ps;
        boolean bs;
    }

}
